import channels_graphql_ws
from dokumentoj.models import DokumentoEkspedo
import graphene
from graphql import GraphQLError

from .schema import DokumentoEkspedoKargoNode, DokumentoEkspedoNode, DokumentoSpecoServoEkspedoNode, DokumentoContractNode, DokumentoNode
from organizoj.models import Organizo, OrganizoMembro
from taskoj.models import TaskojProjekto
from taskoj.api.schema import TaskojProjektoNode



# Реализация подписки для документов экспедирования
# Объединение возвращаемых объектов действий
class DokumentoEventojUnion(graphene.Union):
    class Meta:
        # Типы объединения
        types = (
            DokumentoEkspedoKargoNode,
            DokumentoEkspedoNode,
            DokumentoSpecoServoEkspedoNode,
            DokumentoContractNode,
            TaskojProjektoNode,
        )


# Подписки для WebSocket
class DokumentoEkspedoEventoj(channels_graphql_ws.Subscription):
    """Подписка на изминения в документах экспедирования и связях с ними"""
    evento = graphene.String(required=True)
    dokumento = graphene.relay.node.Field(DokumentoNode)
    objekto = graphene.Field(DokumentoEventojUnion)

    class Arguments:
        """Тут аргументы, передающиеся для подписки на документов экспедирования"""
        organizoj_uuid = graphene.List(graphene.String, required=True)

    def subscribe(self, info, organizoj_uuid):
        """В этом методе определятся список организаций для подписки на изменения документов экспедирования по ним"""
        uzanto = info.context.user
        if info.context.user.is_authenticated:
            # выбираем список членства в организациях из списка организаций
            organizoj_res = OrganizoMembro.objects.filter(organizo__uuid__in=organizoj_uuid, uzanto=uzanto, forigo=False,
                                                    arkivo=False, publikigo=True)
            
            organizoj_set = set(
                organizoj_res.values_list('organizo__uuid', flat=True)
            )

            if not organizoj_res:
                raise GraphQLError(
                    'Невозможно отслеживать организации c UUID {}, они не сущствуют или не доступны'.format(
                        set(organizoj_uuid) - organizoj_set
                    )
                )

            return list(map(lambda v: 'DokumentoEkspedo_{}'.format(v), organizoj_set)) or None

        raise GraphQLError('Необходима авторизация')

    @staticmethod
    def publish(payload, info, organizoj_uuid):
        evento = payload.get('evento')

        try:
            if evento == 'dokumento_ekspedo':
                objekto = DokumentoEkspedo.objects.get(
                    uuid=payload.get('dokumento_ekspedo'),
                    publikigo=True,
                    arkivo=False,
                    forigo=False
                )
                dokumento = objekto.dokumento
            elif evento == 'dokumento_ekspedo_projekto':
                objekto = TaskojProjekto.objects.get(
                    uuid=payload.get('projekto'),
                    publikigo=True,
                    arkivo=False,
                    forigo=False
                )
                dokumento_ekspedo = DokumentoEkspedo.objects.get(
                    uuid=payload.get('dokumento_ekspedo'),
                    publikigo=True,
                    arkivo=False,
                    forigo=False
                )
                dokumento = dokumento_ekspedo.dokumento

            return DokumentoEkspedoEventoj(evento=evento, dokumento=dokumento, objekto=objekto)
        except (Organizo.DoesNotExist, DokumentoEkspedo.DoesNotExist):
            pass

        return DokumentoEkspedoEventoj.SKIP

    @classmethod
    def dokumento_ekspedo(cls, organizo, dokumento_ekspedo):
        """Этот метод должен вызываться при изминении записи в DokumentoEkspedo"""
        cls.broadcast(
            group='DokumentoEkspedo_{}'.format(organizo.uuid),
            payload={
                'evento': 'dokumento_ekspedo',
                'organizo': str(organizo.uuid),
                'dokumento_ekspedo': str(dokumento_ekspedo.uuid)
            }
        )

    @classmethod
    def dokumento_projekto(cls, organizo, dokumento_ekspedo, projekto):
        """Этот метод должен вызываться при изминении записи в DokumentoEkspedo"""
        cls.broadcast(
            group='DokumentoEkspedo_{}'.format(organizo.uuid),
            payload={
                'evento': 'dokumento_ekspedo_projekto',
                'organizo': str(organizo.uuid),
                'dokumento_ekspedo': str(dokumento_ekspedo.uuid),
                'projekto': str(projekto.uuid)
            }
        )


class DokumentoSubscription(graphene.ObjectType):
    dokumento_ekspedo_eventoj = DokumentoEkspedoEventoj.Field()
